#include <memory>
#include <sstream>
#define _CRT_SECURE_NO_WARNINGS
#include "Compilation.hpp"

#include "Symbols/VariableMap.hpp"

#include <Binding/Binder.hpp>
#ifdef _GLIBCXX_NO_ASSERT
#include <crtdbg.h>
#endif
#include "Compilation.hpp"
#include "Text/SourceText.hpp"
#include <cstdlib>
#include <iostream>
#include <ostream>
#include <string>
#include <vector>

bool showtree                 = false;
bool showProgram              = false;

static Compilation *previous  = nullptr;
static auto         variables = std::make_shared<VariableMap>();

bool runSourceText(std::string code)
{
    SourceText text = SourceText::From(code);

    auto syntaxtree = SyntaxTree::Parse(text);
    if (!syntaxtree->Diagnostics->Diagnostics().empty())
    {
        return false;
    }

    auto compilation = previous == nullptr ? new Compilation(syntaxtree) : previous->ContinueWith(syntaxtree);

    if (showtree)
        std::cout << syntaxtree->Root->ToString() << std::endl;
    if (showProgram)
    {
        std::stringstream os;
        compilation->EmitTree(os);
        std::cout << os.str() << std::endl;
    }
    auto result = compilation->Evaluate(variables);
    
    if (result.Diagnostics == nullptr || result.Diagnostics->Diagnostics().empty())
    {
        std::cout << result.Value->ToString() << std::endl;
        previous = compilation;
    }
    else
    {
        for (auto d : result.Diagnostics->Diagnostics())
        {
            auto lineIndex  = text.GetLineIndex(d->Span.Start);
            auto line       = text.GetLines()[lineIndex];

            auto lineNumber = lineIndex + 1;
            auto character  = d->Span.Start - line.Start + 1;

            std::cout << "(" << lineNumber << "," << character << "):" << d->ToString() << std::endl;
        }
    }

    return true;
}
void runPrompt()
{
    std::string textString;
    while (true)
    {
        if (textString.empty())
            std::cout << "> ";
        else
            std::cout << "| ";

        std::string input;
        std::getline(std::cin, input);

        if (textString.empty())
        {
            if (input == "")
            {
                continue;
            }
            if (input == "#showtree")
            {
                showtree = !showtree;
                auto str = showtree ? "Showing parse trees." : "Not showing parse trees";
                std::cout << str << std::endl;
                continue;
            }
            if (input == "#showprogram")
            {
                showProgram = !showProgram;
                auto str    = showProgram ? "Showing bound tree." : "Not showing bound tree.";
                std::cout << str << std::endl;
                continue;
            }
            if (input == "#cls")
            {
                system("cls");
                continue;
            }
            if (input == "#quit")
            {
                break;
            }
            if (input == "#reset")
            {
                delete previous;
                previous = nullptr;
                variables->Clear();
                continue;
            }
        }
        if (!textString.empty())
            textString.append("\n");
        textString.append(input);

        if ((input != "") && !runSourceText(textString))
            continue;

        textString.clear();
    }
}

int main()
{
    runPrompt();
#if 0
  // Parser code("([)(yy)](123456798)//5455454\n/*abfff*/+//SASDASDASDGG");
  do {
    SyntaxToken currToken = code.NextToken();

    std::cout << currToken.toString() << std::endl;

    if (currToken.isMatch(
            {SyntaxKind::Eof, SyntaxKind::Unkown}))
      break;
  } while (1);
#endif
    //_CrtDumpMemoryLeaks();
    return 0;
}
